package com.xd.gateway.config.filter;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.xd.common.exception.TokenException;
import com.xd.common.constant.CommonConstant;
import com.xd.common.constant.GatewayConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;

import java.net.URI;

/**
 * @author huboxin
 * @title: TokenResponseGlobalFilter
 * @projectName bk
 * @description: TokenResponseGlobalFilter
 * @date 2020/6/2915:04
 */
@Slf4j
public class TokenResponseGlobalFilter implements GlobalFilter, Ordered {

    private final RedisTemplate redisTemplate;

    public TokenResponseGlobalFilter(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        URI uri = request.getURI();
        MultiValueMap<String, HttpCookie> cookies = request.getCookies();
        String jti = null;
        if (!ObjectUtil.isEmpty(cookies)) {
            HttpCookie access_token = cookies.getFirst("access_token");
            if (ObjectUtil.isEmpty(access_token)) {
                jti = access_token.getValue();
            }
        }
        if (StrUtil.containsAnyIgnoreCase(uri.getPath(), GatewayConstant.OAUTH_TOKEN_URL)) {
            String grantType = request.getQueryParams()
                    .getFirst(GatewayConstant.GRANT_TYPE);
            // 授权类型为refresh_token
            if (GatewayConstant.GRANT_TYPE_REFRESH_TOKEN.equals(grantType)) {
                // 获取请求里refresh_token
                String refreshToken = request.getQueryParams()
                        .getFirst(GatewayConstant.GRANT_TYPE_REFRESH_TOKEN);
                // 根据jti从Redis里获取真正的refresh_token
                Object object = redisTemplate.opsForValue().get(GatewayConstant.GATEWAY_REFRESH_TOKENS + refreshToken);
                refreshToken = object == null ? refreshToken : object.toString();
                // 替换refresh_token参数
                URI newUri = UriComponentsBuilder.fromUri(uri).replaceQueryParam(GatewayConstant.GRANT_TYPE_REFRESH_TOKEN, refreshToken).build(true).toUri();
                ServerHttpRequest newRequest = exchange.getRequest().mutate().uri(newUri).build();
                return chain.filter(exchange.mutate().request(newRequest).build());
            }
        } else if (!StrUtil.isEmpty(jti) && StringUtils.isNotBlank(jti)) {
            Object object = redisTemplate.opsForValue()
                    .get(GatewayConstant.GATEWAY_ACCESS_TOKENS + jti);
            // 缓存里没有access_token，抛异常，统一异常处理会返回403，前端自动重新获取access_token
            if (object == null)
                throw new TokenException("验证信息失效,请重新登陆");
            String authorization = CommonConstant.TOKEN_SPLIT + object.toString();
            ServerHttpRequest build = request.mutate()
                    .headers(httpHeaders -> httpHeaders.set(CommonConstant.REQ_HEADER, authorization))
                    .build();
            return chain.filter(exchange.mutate()
                    .request(build)
                    .build());
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 2;
    }
}
